/*****************************************************************************/
/**
 *  @file   PixelDraw.h
 *  @author Allan Lorant, Naohisa Sakamoto
 */
/*----------------------------------------------------------------------------
 *
 *  Copyright (c) Visualization Laboratory, Kyoto University.
 *  All rights reserved.
 *  See http://www.viz.media.kyoto-u.ac.jp/kvs/copyright/ for details.
 *
 *  $Id$
 */
/*****************************************************************************/
#ifndef PIXEL_DRAW_H_INCLUDE
#define PIXEL_DRAW_H_INCLUDE

#include <cstring>
#include <iostream>
#include <kvs/glut/GLUT>
#include <kvs/InitializeEventListener>
#include <kvs/PaintEventListener>
#include <kvs/TimeEvent>
#include <flowvr/module.h>
#include <flowvr/trace.h>
#include <ftl/chunkevents.h>
#include "InterfaceBase.h"


namespace PixelDraw
{

class Interface : public InterfaceBase
{
private:

    int m_size[2];
    unsigned char* m_pixel;

    flowvr::InputPort* m_pixel_port;
    flowvr::InputPort* m_size_port;

public:

    Interface( const int width, const int height )
    {
        m_size[0] = width;
        m_size[1] = height;
        m_pixel = new unsigned char [ width * height * 3 ];
        std::memset( m_pixel, 0, width * height * 3 );

        // Declare user defined ports :
        m_pixel_port = new flowvr::InputPort( "pixelIn" );
        m_size_port = new flowvr::InputPort( "sizeIn" );

        std::vector<flowvr::Port*> ports;
        ports.push_back( m_pixel_port );
        ports.push_back( m_size_port );

        if ( InterfaceBase::init( ports ) )
        {
            std::cout << "Can't initialize flowVR module !" << std::endl;
        }
    }

    ~Interface()
    {
        if ( m_pixel ) delete [] m_pixel;
        if ( m_pixel_port ) delete m_pixel_port;
        if ( m_size_port ) delete m_size_port;
    }

    void draw()
    {
        glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
        glDrawPixels( m_size[0], m_size[1], GL_RGB, GL_UNSIGNED_BYTE, m_pixel );
        glutSwapBuffers();
    }

    void redraw()
    {
        glutPostRedisplay();
    }

    bool recv()
    {
        if ( !InterfaceBase::wait() ) { return false; }

        flowvr::Message size;
        size.data = InterfaceBase::alloc( 2 * sizeof(int) );
        if ( size.data.empty() )
        {
            std::cout << "[Send]Alloc error size" << std::endl;
            return false;
        }

        flowvr::Message pixel;
        pixel.data = InterfaceBase::alloc( m_size[0] * m_size[1] * 3 );
        if ( pixel.data.empty() )
        {
            std::cout << "[Send]Alloc error framebuffer" << std::endl;
            return false;
        }

        // Read the data
        InterfaceBase::get( "sizeIn", size );
        InterfaceBase::get( "pixelIn", pixel );

        // Copy the read data from the messages
        std::memcpy( m_size, size.data.getRead<int>(), size.data.getSize() );
        std::memcpy( m_pixel, pixel.data.getRead<unsigned char>(), pixel.data.getSize() );

        size.data.clear();
        pixel.data.clear();

        return true;
    }
};

class InitializeEvent : public kvs::InitializeEventListener
{
    static Interface* m_interface;

public:

    InitializeEvent( Interface* interface )
    {
        m_interface = interface;
    }

    void update()
    {
        glutIdleFunc( idle_func );
    }

private:

    static void idle_func()
    {
        m_interface->recv();
        m_interface->redraw();
    }
};

Interface* InitializeEvent::m_interface = 0;

class PaintEvent : public kvs::PaintEventListener
{
    Interface* m_interface;

public:

    PaintEvent( Interface* interface ):
        m_interface( interface ) {}

    void update()
    {
        int width = glutGet( GLUT_WINDOW_WIDTH );
        int height = glutGet( GLUT_WINDOW_HEIGHT );
        glViewport( 0, 0, width, height );
        glClear( GL_COLOR_BUFFER_BIT );

        m_interface->draw();
    }
};

} // end of namespace PixelDraw

#endif // PIXEL_DRAW_H_INCLUDE
